package com.marchsoft.group.manager.security.Filter;

import com.marchsoft.group.manager.common.enums.ResultEnum;
import com.marchsoft.group.manager.common.exception.BadRequestException;
import com.marchsoft.group.manager.security.Utils.JwtUtils;
import com.marchsoft.group.manager.security.service.JwtUserDetailsService;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


/**
 * fetch
 *
 * @Author zhanghaoqi
 * @Description 拦截器中获取前端发送的token信息
 * @Data 17:33 2020/12/18
 */
@Slf4j
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {

    @Autowired
    //登陆者的信息
            JwtUserDetailsService jwtUserDetailsService;
    private String tokenHeader = "token";
    private String tokenHead = "Bearer ";


    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("进入token过滤器");
        String authHeader = httpServletRequest.getHeader(tokenHeader);
        //如果token非空，且是由tokenHead为头信息，则说明含有token
        if (authHeader != null && authHeader.startsWith(tokenHead)) {
            //获取请求头的token信息
            String authToken = authHeader.substring(tokenHead.length());
            //验证token，不正确或超时则报错，存在则继续向下执行
            Claims claims = null;
            try {
                claims = JwtUtils.parseJWT(authToken);
            } catch (SignatureException e) {
                log.info("token签名有无");
//                throw new BadRequestException(ResultEnum.TOKEN_ERROE);
                resolver.resolveException(httpServletRequest, httpServletResponse,
                        null, new BadRequestException(ResultEnum.TOKEN_ERROE));
                return;//捕获异常后就直接推出了
            } catch (MalformedJwtException e) {
                log.info("token头信息或负载不正确");
//                throw new BadRequestException(ResultEnum.TOKEN_ERROE);
                resolver.resolveException(httpServletRequest, httpServletResponse,
                        null, new BadRequestException(ResultEnum.TOKEN_ERROE));
                return;//捕获异常后就直接推出了
            } catch (ExpiredJwtException e) {
                log.info("token过期");
//                throw new BadRequestException(ResultEnum.TOKEN_EXPIRED);
                resolver.resolveException(httpServletRequest, httpServletResponse,
                        null, new BadRequestException(ResultEnum.TOKEN_EXPIRED));
                return;//捕获异常后就直接推出了
            } catch (Exception e) {
                log.info("token错误");
                resolver.resolveException(httpServletRequest, httpServletResponse,
                        null, new BadRequestException(ResultEnum.TOKEN_ERROE));
                return;//捕获异常后就直接推出了

//                throw new BadRequestException(ResultEnum.TOKEN_ERROE);
            }
            //获取登陆者的用户名
            String username = claims.getId();
            //验证token,具体怎么验证看需求，可以只验证token不查库，把权限放在jwt中即可
            UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);
            if (userDetails != null) {
                //这里只要告诉springsecurity权限即可，账户密码就不用提供验证了，这里我们把UserDetails传给springsecurity，以便以后我们获取当前登录用户
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                //UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null, UserDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                logger.info(String.format("Authenticated userDetail %s, setting security context", username));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}